package com.seewo.dubbo.debugger.common;

import com.alibaba.fastjson.JSONObject;

import java.io.*;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author llchen12
 * @date 2018/6/26
 */
public class CommentParse {

    private static final int STATUS_COMMENT = 0;
    private static final int STATUS_PARAM = 1;
    private static final int STATUS_RETURN = 2;
    private static final int STATUS_OTHER = 3;

    private static final String SPACE_STRING = " ";
    private static final String NEWLINE_STRING = "\n";
    private static final String SEPARATE_STRING = "@";
    private static final String PARAM_STRING = "@param";
    private static final String RETURN_STRING = "@return";


    private Map<String, AnnotatedClass> docClassMap = new HashMap<>(64);


    public CommentParse(String json) {
        parseJson(json);
    }


    public static CommentParse load(String file) {
        try (InputStream in = new FileInputStream(file)) {
            return load(in);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static CommentParse loadDefault() {
        InputStream is = CommentParse.class.getResourceAsStream("/META-INF/doc-comment.json");
        return is == null ? null : load(is);
    }


    public static CommentParse load(InputStream in) {
        CommentParse commentParse = null;
        try (InputStreamReader isr = new InputStreamReader(in, "UTF-8");
             BufferedReader reader = new BufferedReader(isr)) {

            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            commentParse = new CommentParse(sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return commentParse;
    }


    private void parseJson(String json) {
        JSONObject jsonObj = JSONObject.parseObject(json);
        for (String key : jsonObj.keySet()) {
            try {
                AnnotatedClass aClass = jsonObj.getObject(key, AnnotatedClass.class);
                docClassMap.put(key, aClass);
            } catch (Exception e) {
                System.err.println("解析json异常:" + e.getMessage());
            }
        }
    }

    public AnnotatedClass getAnnotatedClass(String className) {
        return this.docClassMap.get(className);
    }

    public AnnotatedClass getAnnotatedClass(Class<?> clazz) {
        return getAnnotatedClass(clazz.getCanonicalName());
    }

    public String getClassComment(Class<?> clazz) {
        AnnotatedClass aClass = getAnnotatedClass(clazz);
        if (aClass != null) {
            Comment comment = parseDoc(aClass.getDoc());
            return comment.getComment();
        }
        return "";
    }

    public AnnotatedMethod getAnnotatedMethod(String className, Method namedMethod) {
        AnnotatedClass aClass = getAnnotatedClass(className);
        if (aClass != null) {
            List<AnnotatedMethod> methods = aClass.getMethods();
            for (AnnotatedMethod method : methods) {
                String methodName = method.getMethodName();
                if (methodName.equals(Util.getMethodName(namedMethod))) {
                    return method;
                }
            }
        }
        return null;
    }


    public AnnotatedMethod getAnnotatedMethod(Class<?> clazz, Method method) {
        return getAnnotatedMethod(clazz.getCanonicalName(), method);
    }

    public AnnotatedMethod getAnnotatedMethod(Method method) {
        return getAnnotatedMethod(method.getDeclaringClass(), method);
    }


    public Comment getMethodComment(Method method) {
        AnnotatedMethod annotatedMethod = getAnnotatedMethod(method);
        if (annotatedMethod != null) {
            return parseDoc(annotatedMethod.getDoc());
        }
        return null;
    }


    public static Comment parseDoc(String doc) {
        Comment comment = new Comment();
        if (doc == null || "".equals(doc.trim())) {
            return comment;
        }
        String[] str = doc.split(NEWLINE_STRING);
        int status = STATUS_COMMENT;
        String temp = "";
        for (String line : str) {
            line = line.trim();
            if ("".equals(line)) {
                continue;
            }
            if (line.startsWith(SEPARATE_STRING)) {
                int i = line.indexOf(SPACE_STRING);
                String sp = line.substring(i + 1).trim();
                if (line.startsWith(PARAM_STRING)) {
                    status = STATUS_PARAM;
                    int dx = sp.indexOf(SPACE_STRING);
                    String param = sp.substring(0, dx);
                    String desc = sp.substring(dx + 1).trim();
                    comment.addParam(param, desc);
                    temp = param;
                } else if (line.startsWith(RETURN_STRING)) {
                    status = STATUS_RETURN;
                    comment.setRet(sp);
                } else {
                    status = STATUS_OTHER;
                }
            } else {
                switch (status) {
                    case STATUS_COMMENT:
                        String cmt = comment.getComment() + SPACE_STRING + line;
                        comment.setComment(cmt.trim());
                        break;
                    case STATUS_PARAM:
                        String desc = comment.getParam(temp) + SPACE_STRING + line;
                        comment.addParam(temp, desc.trim());
                        break;
                    case STATUS_RETURN:
                        String ret = comment.getRet() + SPACE_STRING + line;
                        comment.setRet(ret.trim());
                        break;
                    default:
                        break;
                }
            }
        }
        return comment;
    }

}
